package com.data.common.interceptor;


import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.TableNameParser;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;
import com.data.common.entity.OperationDataChange;
import com.data.common.util.DataOperateLogThreadLocal;
import com.data.common.util.SpringContextHolder;
import com.mysql.cj.jdbc.ClientPreparedStatement;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.sql.Statement;
import java.util.*;

/**
 * 数据更新拦截器 数据变更审计
 */
@SuppressWarnings("ALL")
@Slf4j
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "update", args = {Statement.class})})
public class DataOperateInterceptor extends AbstractSqlParserHandler implements Interceptor {

    @Autowired
    @Qualifier("jdbcTemplateOne")
    private JdbcTemplate jdbcTemplate;


    @Override
    public Object intercept(Invocation invocation) throws Exception {
        String originalSql = null;
        try{
            // 判断是否需要记录日志
            if (DataOperateLogThreadLocal.DATA_CHANGES.get() == null) {
                return invocation.proceed();
            }
            StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
            MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
            this.sqlParser(metaObject);
            MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
            if(!SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) {
                //非更新数据
                return invocation.proceed();
            }

            if(jdbcTemplate == null){
                jdbcTemplate = SpringContextHolder.getBean(JdbcTemplate.class);
            }
            Object firstArg = invocation.getArgs()[0];
            Statement statement = (Statement) firstArg;
            MetaObject stmtMetaObj = SystemMetaObject.forObject(statement);
            // 分离代理对象链(由于目标类可能被多个拦截器拦截，从而形成多次代理，通过下面的两次循环可以分离出最原始的的目标类)
            while (stmtMetaObj.hasGetter("h")) {
                Object object = stmtMetaObj.getValue("h");
                stmtMetaObj = SystemMetaObject.forObject(object);
            }
            // 分离最后一个代理对象的目标类
            while (stmtMetaObj.hasGetter("target")) {
                Object object = stmtMetaObj.getValue("target");
                stmtMetaObj = SystemMetaObject.forObject(object);
            }
            try {
                statement = (Statement) stmtMetaObj.getValue("statement");
                /*List<Object> columnValues = (List<Object>) stmtMetaObj.getValue("columnValues");
                Map<Integer,Object> paramerters = (Map<Integer, Object>) stmtMetaObj.getValue("statement.stmt.parameters");
                /*Object objSql = stmtMetaObj.getValue("statement.stmt.sql");
                //获取业务要执行的sql
                originalSql = objSql.toString();
                originalSql = originalSql.replaceAll("[\\s]+", StringPool.SPACE);
                //TODO 组装参数
                originalSql = makeupExeSQL(originalSql,columnValues,paramerters);*/
                Object raw = stmtMetaObj.getValue("statement.stmt.raw");
                ClientPreparedStatement cps = (ClientPreparedStatement) raw;
                originalSql = cps.asSql();
                originalSql = originalSql.replaceAll("[\\s]+", StringPool.SPACE);
                log.info("raw----sql:{}",originalSql);

            } catch (Exception e) {
            }
            originalSql = originalSql.replaceAll("[\\s]+", StringPool.SPACE);
            int index = indexOfSqlStart(originalSql);
            if (index > 0) {
                originalSql = originalSql.substring(index);
            }
            // 获取执行Sql
            String sql = originalSql.replace("where", "WHERE");
            if(sql.endsWith("]")){
                sql = sql.replaceAll("]","");
            }
            // 使用mybatis-plus 工具解析sql获取表名
            Collection<String> tables = new TableNameParser(sql).tables();
            if (CollectionUtils.isEmpty(tables) || sql.indexOf("WHERE") < 0) {
                return invocation.proceed();
            }

            String tableName = tables.iterator().next();
            OperationDataChange change = new OperationDataChange();
            //change.setDataId(EasyMsTraceSynchronizationManager.getTraceId());
            change.setTableName(tableName);
            change.setJdbcTemplate(jdbcTemplate);
            log.info("====biz pre exec sql:{}",sql);
            // 设置sql用于执行完后查询新数据
            String whereSql =  sql.substring(sql.lastIndexOf("WHERE") + 5);
            // 同表对同条数据操作多次只进行一次对比
            if (DataOperateLogThreadLocal.DATA_CHANGES.get().stream().anyMatch(c -> tableName.equals(c.getTableName())
                    && whereSql.equals(c.getWhereSql()))) {
                return invocation.proceed();
            }
            change.setWhereSql(whereSql);
            String querySql = "select * from "+tableName+" where "+whereSql;

            TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);
            List<TableFieldInfo> fieldList = tableInfo.getFieldList();
            String selectSql = tableInfo.getAllSqlSelect();
            StringBuffer sb = new StringBuffer();
            String[] realStrs = selectSql.split(",");
            for(int i =0;i < realStrs.length;i ++){
                String column = realStrs[i];
                //UPDATE sys_user SET org_code = 'A01A01A14' WHERE username = '13235654896'
                if(sql.toLowerCase().indexOf(" "+column+" ") >=0 ){
                    sb.append(column).append(",");
                }
            }
            //明确 具体的查询的字段
            if(sb.toString().endsWith(",")){
                querySql = "select "+sb.toString().substring(0,sb.length() - 1)+" from "+ tableName+" where "+whereSql;
            }
            log.info("====querySql:{}",querySql);
            change.setQuerySql(querySql);
            change.setTransferData(fieldList);
            if(tableInfo.getEntityType() == null){
                List<Map<String, Object>> maps = jdbcTemplate.queryForList(querySql);
                change.setOldData(maps);
            }else{
                String clzzName = tableInfo.getEntityType().getName();
                Class<?> clazz = Class.forName(clzzName);
                List<?> oldList = jdbcTemplate.query(querySql,new BeanPropertyRowMapper<>(clazz));
                change.setOldData(oldList);
            }
            change.setEntityType(tableInfo.getEntityType());
            DataOperateLogThreadLocal.DATA_CHANGES.get().add(change);
        }catch (Exception e){
            log.info("DataOperateInterceptor-intercept-error:{}", JSON.toJSONString(e));
        }
        return invocation.proceed();
    }

    private String makeupExeSQL(String originalSql, List<Object> columnValues, Map<Integer, Object> paramerters) {
        if(originalSql.indexOf(" where ") >=0 ){
            originalSql = originalSql.replace(" where "," WHERE ");
        }
        if(originalSql.indexOf("? WHERE ") >=0 ){
            originalSql = originalSql.replace("? WHERE ","?, WHERE ");
        }
        StringBuffer sb = new StringBuffer(originalSql);
        sb.append(",");
        String sql = sb.toString();
        log.info("sql:{}",sql);
        //参数数量
        // 入参个数与执行sql的占位符？, 相等
        int paramSize = columnValues.size();
        if(paramSize == 0){
            return originalSql;
        }
        for(int i = 0;i < paramSize;i ++){
            Object obj = columnValues.get(i);
            JdbcParameter param = (JdbcParameter) paramerters.get(i);
            String pa = param.getValue().toString();
            obj.toString();
            StringBuffer str = new StringBuffer("'").append(pa).append("',");
            sql = sql.replaceFirst("\\?,",str.toString());
        }
        if(sql.endsWith(",")){
            sql = sql.substring(0,sql.length() - 1);
            sql = sql.replace(", WHERE "," WHERE ");
        }
        log.info("makeupExeSQL sql:{}",sql);
        return sql;
    }

    /**
     * 获取sql语句开头部分
     */
    private int indexOfSqlStart(String sql) {
        String upperCaseSql = sql.toUpperCase();
        Set<Integer> set = new HashSet<>();
        set.add(upperCaseSql.indexOf("SELECT "));
        set.add(upperCaseSql.indexOf("UPDATE "));
        set.add(upperCaseSql.indexOf("INSERT "));
        set.add(upperCaseSql.indexOf("DELETE "));
        set.remove(-1);
        if (CollectionUtils.isEmpty(set)) {
            return -1;
        }
        List<Integer> list = new ArrayList<>(set);
        list.sort(Comparator.naturalOrder());
        return list.get(0);
    }

    public static void main(String[] args){
        String originalSql = "UPDATE demo SET name = 1, key_word = ?, punch_time = ?, sex = ?, age = ?, birthday = ?, content = ?, create_by = ?, create_time = ?, update_by = ?, update_time = ?, sys_org_code = ?, tenant_id = 0 WHERE tenant_id = 0 AND id = ?";
        Collection<String> tables = new TableNameParser(originalSql).tables();
        TableInfo tableInfo = TableInfoHelper.getTableInfo("demo");
        List<String> column = SqlUtils.findPlaceholder("select name from demo");
        System.out.println("==column:"+ JSON.toJSON(column));
        //originalSql = "UPDATE sys_user SET org_code = 0 WHERE username = ?";
        if(originalSql.indexOf(" where ") >=0 ){
            originalSql = originalSql.replace(" where "," WHERE ");
        }
        if(originalSql.indexOf(" WHERE ") >=0 ){
            originalSql = originalSql.replace(" WHERE ",", WHERE ");
        }
        if(originalSql.indexOf(" set ") >=0 ){
            originalSql = originalSql.replace(" set "," SET ");
        }
        StringBuffer sb = new StringBuffer(originalSql);
        sb.append(",");
        String sql = sb.toString();
        //TODO 获取更新的表字段
        String queryColumn = sql.substring(sql.indexOf(" SET ")+" SET ".length(),sql.indexOf(", WHERE") +1);
        queryColumn = queryColumn.replaceAll(" = \\?","");
        if(queryColumn.endsWith(",")){
            queryColumn = " "+queryColumn.substring(0,queryColumn.length() -1);
        }
        System.out.println("queryColumn:"+queryColumn);

        //还原可执行sql
        if(sql.endsWith(",")){
            sql = sql.substring(0,sql.length() - 1);
            sql = sql.replace(", WHERE "," WHERE ");
        }
        System.out.println("originalSql:"+originalSql);
        System.out.println("sql:"+sql);
    }
}